//
// FieldDefinition.cs
//
// Author:
//   Jb Evain (jbevain@gmail.com)
//
// (C) 2005 - 2008  Jb Evain
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

using System;

namespace Mono.Cecil {

	public sealed class FieldDefinition : FieldReference, IMemberDefinition,
		ICustomAttributeProvider, IConstantProvider/*, IHasMarshalSpec, */ {

		FieldAttributes attributes;

		CustomAttributeCollection custom_attributes;

		int? offset;

		int? rva;
		byte [] initial_value;

		object constant = Constants.NotResolved;

		// MarshalSpec m_marshalDesc;

		public bool HasLayoutInfo {
			get {
				if (offset.HasValue)
					return true;

				if (HasImage) {
					offset = DeclaringType.Module.Read (this, (field, reader) => reader.ReadFieldLayout (field));
					return offset.HasValue;
				}

				return false;
			}
		}

		public int Offset {
			get {
				if (offset.HasValue)
					return offset.Value;

				if (HasImage) {
					offset = DeclaringType.Module.Read (this, (field, reader) => reader.ReadFieldLayout (field));
					return offset ?? 0;
				}

				return 0;
			}
			set { offset = value; }
		}

		public int RVA {
			get {
				if (rva.HasValue)
					return rva.Value;

				if (HasImage) {
					rva = DeclaringType.Module.Read (this, (field, reader) => reader.ReadFieldRVA (field));
					return rva.Value;
				}

				rva = 0;
				return 0;

			}
			internal set { rva = value; }
		}

		public byte [] InitialValue {
			get {
				if (initial_value != null)
					return initial_value;

				if (HasImage) {
					DeclaringType.Module.Read (this, (field, reader) => reader.ReadFieldRVA (field));
					return initial_value;
				}

				return initial_value = Empty<byte>.Array;
			}
			set { initial_value = value; }
		}

		public FieldAttributes Attributes {
			get { return attributes; }
			set { attributes = value; }
		}

		public bool HasConstant {
			get {
				ResolveConstant ();

				return constant != Constants.NoValue;
			}
			set { if (!value) constant = Constants.NoValue; }
		}

		public object Constant {
			get { return HasConstant ? constant : null;	}
			set { constant = value; }
		}

		void ResolveConstant ()
		{
			if (constant != Constants.NotResolved)
				return;

			if (!HasImage) {
				constant = Constants.NoValue;
				return;
			}

			constant = DeclaringType.Module.Read (this, (field, reader) => reader.ReadConstant (field));
		}

		public bool HasCustomAttributes {
			get {
				if (custom_attributes != null)
					return custom_attributes.Count > 0;

				if (HasImage)
					return DeclaringType.Module.Read (this, (type, reader) => reader.HasCustomAttributes (type));

				return false;
			}
		}

		public CustomAttributeCollection CustomAttributes {
			get {
				if (custom_attributes != null)
					return custom_attributes;

				if (HasImage)
					return custom_attributes = DeclaringType.Module.Read (this, (type, reader) => reader.ReadCustomAttributes (type));

				return custom_attributes = new CustomAttributeCollection ();
			}
		}

		/*
		public MarshalSpec MarshalSpec {
			get { return m_marshalDesc; }
			set {
				m_marshalDesc = value;
				if (value != null)
					attributes |= FieldAttributes.HasFieldMarshal;
				else
					attributes &= FieldAttributes.HasFieldMarshal;
			}
		}
		*/

		#region FieldAttributes

		public bool IsCompilerControlled {
			get { return (attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Compilercontrolled; }
			set {
				if (value) {
					attributes &= ~FieldAttributes.FieldAccessMask;
					attributes |= FieldAttributes.Compilercontrolled;
				} else
					attributes &= ~(FieldAttributes.FieldAccessMask & FieldAttributes.Compilercontrolled);
			}
		}

		public bool IsPrivate {
			get { return (attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Private; }
			set {
				if (value) {
					attributes &= ~FieldAttributes.FieldAccessMask;
					attributes |= FieldAttributes.Private;
				} else
					attributes &= ~(FieldAttributes.FieldAccessMask & FieldAttributes.Private);
			}
		}

		public bool IsFamilyAndAssembly {
			get { return (attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.FamANDAssem; }
			set {
				if (value) {
					attributes &= ~FieldAttributes.FieldAccessMask;
					attributes |= FieldAttributes.FamANDAssem;
				} else
					attributes &= ~(FieldAttributes.FieldAccessMask & FieldAttributes.FamANDAssem);
			}
		}

		public bool IsAssembly {
			get { return (attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Assembly; }
			set {
				if (value) {
					attributes &= ~FieldAttributes.FieldAccessMask;
					attributes |= FieldAttributes.Assembly;
				} else
					attributes &= ~(FieldAttributes.FieldAccessMask & FieldAttributes.Assembly);
			}
		}

		public bool IsFamily {
			get { return (attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Family; }
			set {
				if (value) {
					attributes &= ~FieldAttributes.FieldAccessMask;
					attributes |= FieldAttributes.Family;
				} else
					attributes &= ~(FieldAttributes.FieldAccessMask & FieldAttributes.Family);
			}
		}

		public bool IsFamilyOrAssembly {
			get { return (attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.FamORAssem; }
			set {
				if (value) {
					attributes &= ~FieldAttributes.FieldAccessMask;
					attributes |= FieldAttributes.FamORAssem;
				} else
					attributes &= ~(FieldAttributes.FieldAccessMask & FieldAttributes.FamORAssem);
			}
		}

		public bool IsPublic {
			get { return (attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Public; }
			set {
				if (value) {
					attributes &= ~FieldAttributes.FieldAccessMask;
					attributes |= FieldAttributes.Public;
				} else
					attributes &= ~(FieldAttributes.FieldAccessMask & FieldAttributes.Public);
			}
		}

		public bool IsStatic {
			get { return (attributes & FieldAttributes.Static) != 0; }
			set {
				if (value)
					attributes |= FieldAttributes.Static;
				else
					attributes &= ~FieldAttributes.Static;
			}
		}

		public bool IsInitOnly {
			get { return (attributes & FieldAttributes.InitOnly) != 0; }
			set {
				if (value)
					attributes |= FieldAttributes.InitOnly;
				else
					attributes &= ~FieldAttributes.InitOnly;
			}
		}

		public bool IsLiteral {
			get { return (attributes & FieldAttributes.Literal) != 0; }
			set {
				if (value)
					attributes |= FieldAttributes.Literal;
				else
					attributes &= ~FieldAttributes.Literal;
			}
		}

		public bool IsNotSerialized {
			get { return (attributes & FieldAttributes.NotSerialized) != 0; }
			set {
				if (value)
					attributes |= FieldAttributes.NotSerialized;
				else
					attributes &= ~FieldAttributes.NotSerialized;
			}
		}

		public bool IsSpecialName {
			get { return (attributes & FieldAttributes.SpecialName) != 0; }
			set {
				if (value)
					attributes |= FieldAttributes.SpecialName;
				else
					attributes &= ~FieldAttributes.SpecialName;
			}
		}

		public bool IsPInvokeImpl {
			get { return (attributes & FieldAttributes.PInvokeImpl) != 0; }
			set {
				if (value)
					attributes |= FieldAttributes.PInvokeImpl;
				else
					attributes &= ~FieldAttributes.PInvokeImpl;
			}
		}

		public bool IsRuntimeSpecialName {
			get { return (attributes & FieldAttributes.RTSpecialName) != 0; }
			set {
				if (value)
					attributes |= FieldAttributes.RTSpecialName;
				else
					attributes &= ~FieldAttributes.RTSpecialName;
			}
		}

		public bool HasDefault {
			get { return (attributes & FieldAttributes.HasDefault) != 0; }
			set {
				if (value)
					attributes |= FieldAttributes.HasDefault;
				else
					attributes &= ~FieldAttributes.HasDefault;
			}
		}

		#endregion

		public override bool IsDefinition {
			get { return true; }
		}

		public new TypeDefinition DeclaringType {
			get { return (TypeDefinition) base.DeclaringType; }
			set { base.DeclaringType = value; }
		}

		public FieldDefinition (string name, TypeReference fieldType,
			FieldAttributes attrs) : base (name, fieldType)
		{
			attributes = attrs;
		}

		public override FieldDefinition Resolve ()
		{
			return this;
		}
	}
}
